package org.aphelie.torg.ruler.internal.core.character;

import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Vector;

import org.aphelie.torg.ruler.core.IModel;
import org.aphelie.torg.ruler.core.IModelChangeProvider;
import org.aphelie.torg.ruler.core.IModelChangedEvent;
import org.aphelie.torg.ruler.core.ISourceObject;
import org.aphelie.torg.ruler.core.ModelChangedEvent;
import org.aphelie.torg.ruler.core.character.ICharacterBase;
import org.aphelie.torg.ruler.core.character.ICharacterModelBase;
import org.aphelie.torg.ruler.core.character.ICharacterObject;
import org.aphelie.torg.ruler.core.character.ISharedCharacterModel;
import org.aphelie.torg.ruler.internal.core.TGRCore;
import org.aphelie.torg.ruler.internal.core.TGRCoreMessages;
import org.aphelie.torg.ruler.internal.core.util.TGRXMLHelper;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.core.runtime.Status;
import org.w3c.dom.Comment;
import org.w3c.dom.Node;


public abstract class CharacterObject extends PlatformObject implements ICharacterObject, ISourceObject, Serializable, IWritableDelimiter {
    private static final long serialVersionUID = 1L;

    protected String fName;

    private transient String fTranslatedName;
    private transient ICharacterObject fParent;
    private transient ISharedCharacterModel fModel;
    private transient boolean fInTheModel;

    protected int fStartLine = 1;

    public CharacterObject() {
    }

    public boolean isValid() {
        return true;
    }

    protected void ensureModelEditable() throws CoreException {
        if (!fModel.isEditable()) {
            throwCoreException(TGRCoreMessages.CharacterObject_readOnlyChange);
        }
    }

    public void setInTheModel(boolean value) {
        fInTheModel = value;
    }

    public boolean isInTheModel() {
        return fInTheModel;
    }

    protected void firePropertyChanged(String property, Object oldValue, Object newValue) {
        firePropertyChanged(this, property, oldValue, newValue);
    }

    protected void firePropertyChanged(ICharacterObject object, String property, Object oldValue, Object newValue) {
        if (fModel.isEditable()) {
            IModelChangeProvider provider = fModel;
            provider.fireModelObjectChanged(object, property, oldValue, newValue);
        }
    }

    protected void fireStructureChanged(ICharacterObject child, int changeType) {
        IModel model = getModel();
        if (model.isEditable() && model instanceof IModelChangeProvider) {
            IModelChangedEvent e = new ModelChangedEvent((IModelChangeProvider) model, changeType, new Object[] {child}, null);
            fireModelChanged(e);
        }
    }

    protected void fireStructureChanged(ICharacterObject[] children, int changeType) {
        IModel model = getModel();
        if (model.isEditable() && model instanceof IModelChangeProvider) {
            IModelChangedEvent e = new ModelChangedEvent((IModelChangeProvider) model, changeType, children, null);
            fireModelChanged(e);
        }
    }

    protected void fireModelChanged(IModelChangedEvent e) {
        IModel model = getModel();
        if (model.isEditable() && model instanceof IModelChangeProvider) {
            IModelChangeProvider provider = (IModelChangeProvider) model;
            provider.fireModelChanged(e);
        }
    }

    public ISharedCharacterModel getModel() {
        return fModel;
    }

    public ICharacterModelBase getCharacterModel() {
        /* TODO
        if (fModel instanceof IBundleCharacterModelProvider)
            return ((IBundleCharacterModelProvider) fModel).getBundleCharacterModel();
        */
        return fModel instanceof ICharacterModelBase ? (ICharacterModelBase) fModel : null;
    }

    public String getName() {
        return fName;
    }

    public String getTranslatedName() {
        if (fTranslatedName != null && !fModel.isEditable())
            return fTranslatedName;
        if (fTranslatedName == null && fName != null && fModel != null) {
            fTranslatedName = fModel.getResourceString(fName);
        }
        return fTranslatedName;
    }

    String getNodeAttribute(Node node, String name) {
        Node attribute = node.getAttributes().getNamedItem(name);
        if (attribute != null)
            return attribute.getNodeValue();
        return null;
    }

    public ICharacterObject getParent() {
        return fParent;
    }

    public ICharacterBase getCharacterBase() {
        ICharacterModelBase characterModel = getCharacterModel();
        return characterModel != null ? characterModel.getCharacterBase() : null;
    }

    public String getResourceString(String key) {
        return fModel.getResourceString(key);
    }

    static boolean isNotEmpty(String text) {
        for (int i = 0; i < text.length(); i++) {
            if (java.lang.Character.isWhitespace(text.charAt(i)) == false)
                return true;
        }
        return false;
    }

    public void restoreProperty(String name, Object oldValue, Object newValue) throws CoreException {
        if (name.equals(K_NAME)) {
            setName(newValue != null ? newValue.toString() : null);
        }
    }

    public void setModel(ISharedCharacterModel model) {
        this.fModel = model;
        fTranslatedName = null;
    }

    public void setName(String name) throws CoreException {
        ensureModelEditable();
        String oldValue = this.fName;
        this.fName = name;
        firePropertyChanged(K_NAME, oldValue, name);
    }

    public void setParent(ICharacterObject parent) {
        this.fParent = parent;
    }

    protected void throwCoreException(String message) throws CoreException {
        Status status = new Status(IStatus.ERROR, TGRCore.PLUGIN_ID, IStatus.OK, message, null);
        CoreException ce = new CoreException(status);
        ce.fillInStackTrace();
        throw ce;
    }

    public String toString() {
        String result = null;
        if (fName != null) {
            result = fName;
        }
        if ((result == null || result.indexOf('%') >= 0) && fModel != null) {
            result = fModel.toString();
        }
        if (result != null) {
            return result;
        }
        return super.toString();
    }

    public Vector addComments(Node node, Vector result) {
        for (Node prev = node.getPreviousSibling(); prev != null; prev = prev.getPreviousSibling()) {
            if (prev.getNodeType() == Node.TEXT_NODE)
                continue;
            if (prev instanceof Comment) {
                String comment = prev.getNodeValue();
                if (result == null)
                    result = new Vector();
                result.add(0, comment);
            } else
                break;
        }
        return result;
    }

    void writeComments(PrintWriter writer, Vector source) {
        if (source == null)
            return;
        for (int i = 0; i < source.size(); i++) {
            String comment = (String) source.elementAt(i);
            writer.println("<!--" + comment + "-->"); //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    protected boolean stringEqualWithNull(String a, String b) {
        return a == null && b == null || a != null && b != null && a.equals(b);
    }

    public String getWritableString(String source) {
        return TGRXMLHelper.getWritableString(source);
    }

    public int getStartLine() {
        return fStartLine;
    }

    public int getStopLine() {
        return fStartLine;
    }

    public Object getAdapter(Class adapter) {
        if (adapter.equals(ICharacterModelBase.class)) {
            return getCharacterModel();
        }
        return super.getAdapter(adapter);
    }

    /**
     * @param model
     * @param parent
     */
    public void reconnect(ISharedCharacterModel model, ICharacterObject parent) {
        // Transient Field:  In The Model
        fInTheModel = false;
        // Transient Field:  Model
        fModel = model;
        // Transient Field:  Parent
        fParent = parent;
        // Transient Field:  Translated Name
        fTranslatedName = null;
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.core.plugin.IWritableDelimeter#writeDelimeter(java.io.PrintWriter)
     */
    public void writeDelimeter(PrintWriter writer) {
        // NO-OP
        // Child classes to override
    }

}
